//加锁要保证每一个修改操作都在锁里面
//给要读取的变量都加上volatile
//使用notifyAll会报错,无法在多线程中使用
class MyBlockingQueue{
    private String[] elems = null;
    private volatile int head;
    private volatile int tail;
    private volatile int size;
    public MyBlockingQueue(int capacity){
        elems = new String[capacity];
    }
    void put(String elem) throws InterruptedException {
        synchronized (this){
            //将修改操作都放入锁里面
            while(size>=elem.length()){
                //使用阻塞等待, 要确保阻塞等待的对象和锁的对象是一样的(notify也是一样的)
                this.wait();
            }
            elems[tail] = elem;
            tail++;
            if(tail>=elems.length){
                tail=0;
            }
            size++;
            //唤醒 take 阻塞的情况  为空
            this.notify();
        }
    }
    String take() throws InterruptedException {
        synchronized (this){
            while (size==0){
                //使用while再多判定一次
                //阻塞之后,再去确定一下条件是否成立
                this.wait();
            }
            String result = elems[head];
            head++;if(head>=elems.length){
                head=0;
            }
            size--;
            //take 成功一个元素,就会唤醒 put 中的 wait 操作
            this.notify();
            return result;
        }
    }
}

public class Demo1 {
    public static void main(String[] args) {
        MyBlockingQueue queue = new MyBlockingQueue(1000);
        Thread t1 = new Thread(()->{
            try {
                int count = 1;
                while (true){
                    queue.put(count+"");
                    System.out.println("生产"+count);
                    count++;
                    Thread.sleep(1000);
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(()->{
            try {
                while (true){
                    String result = queue.take();
                    System.out.println("消费"+result);
                    }
                }catch (InterruptedException e){
                e.printStackTrace();
            }
        });

        t1.start();
        t2.start();
    }
}
